package com.ruoyi.system.wnjk.service;

import com.ruoyi.system.wnjk.domain.dto.PhysicalDTO;
import com.ruoyi.system.wnjk.domain.po.Physical;
import com.ruoyi.system.wnjk.mapper.PhysicalMapper;
import com.ruoyi.system.wnjk.mapper.WnjkRegistrationOrderMapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class InventoryService {

    private static final Logger log = LoggerFactory.getLogger(InventoryService.class);

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private PhysicalMapper physicalMapper;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 监听 Spring 容器刷新完成事件，确保所有 Bean 都初始化后再执行
     */
    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("Spring 容器初始化完成，开始执行库存初始化...");
        initializeInventory("Startup Initialization", false); // 启动时不使用锁
    }

    /**
     * 每天凌晨12点重新初始化库存
     */
    @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨12点执行
    public void initializeInventoryDaily() {
        initializeInventory("Daily Initialization", true); // 定时任务使用锁
    }

    /**
     * 库存初始化逻辑
     */
    private void initializeInventory(String context, boolean useLock) {
        RLock lock = useLock ? redissonClient.getLock("inventory:init:lock") : null;
        boolean isLocked = false;

        try {
            // 如果使用锁，尝试获取
            if (useLock && lock != null) {
                isLocked = lock.tryLock(10, 10, TimeUnit.SECONDS);
                if (!isLocked) {
                    log.warn("【库存初始化 - {}】未能获取锁，跳过初始化", context);
                    return;
                }
                log.info("【库存初始化 - {}】尝试获取锁成功，开始初始化", context);
            } else {
                log.info("【库存初始化 - {}】不使用锁，直接开始初始化", context);
            }

            // 清空所有 inventory:* 键
            Set<String> keys = redisTemplate.keys("inventory:*");
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.info("【库存初始化 - {}】已清空旧库存数据: {} 个键", context, keys.size());
            }

            // 从数据库加载所有有效的体检套餐
            PhysicalDTO physicalDTO = new PhysicalDTO();
            physicalDTO.setIsAvailable(1);
            List<Physical> physicals = physicalMapper.listSearch(physicalDTO);
            if (physicals != null && !physicals.isEmpty()) {
                LocalDate tomorrow = LocalDate.now().plusDays(1);
                for (Physical physical : physicals) {
                    if (physical.getIsAvailable() == 1) {
                        Long physicalId = physical.getId();
                        Long total = physical.getTotal() != null ? physical.getTotal().longValue() : 0L;
                        String amKey = buildInventoryKey(physicalId, tomorrow, "AM");
                        String pmKey = buildInventoryKey(physicalId, tomorrow, "PM");
                        redisTemplate.opsForValue().set(amKey, total.toString());
                        redisTemplate.opsForValue().set(pmKey, total.toString());
                        log.info("【库存初始化 - {}】初始化套餐 {}，日期 {}，AM/PM 库存: {}", context, physicalId, tomorrow, total);
                    }
                }
            } else {
                log.info("【库存初始化 - {}】未找到有效的体检套餐数据", context);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("【库存初始化 - {}】异常: {}", context, e.getMessage(), e);
        } catch (Exception e) {
            log.error("【库存初始化 - {}】未捕获异常: {}", context, e.getMessage(), e);
        } finally {
            // 仅在获取锁成功的情况下释放锁
            if (useLock && isLocked && lock != null) {
                try {
                    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                        lock.unlock();
                        log.info("【库存初始化 - {}】释放锁", context);
                    } else {
                        log.warn("【库存初始化 - {}】锁未被当前线程持有，跳过释放", context);
                    }
                } catch (Exception e) {
                    log.error("【库存初始化 - {}】释放锁失败: {}", context, e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 查询可用库存
     */
    public Long getAvailableStock(Long physicalId, LocalDate date, String period) {
        String key = buildInventoryKey(physicalId, date, period);
        initializeInventoryIfAbsent(key, physicalId);
        Object availableObj = redisTemplate.opsForValue().get(key);
        return availableObj != null ? Long.parseLong(availableObj.toString()) : 0L;
    }

    /**
     * 提交订单时减少库存（带分布式锁）
     */
    public boolean deductStock(Long physicalId, LocalDate date, String period, String outTradeNo) {
        String key = buildInventoryKey(physicalId, date, period);
        String lockKey = "inventory_lock:" + key;

        initializeInventoryIfAbsent(key, physicalId);

        RLock lock = redissonClient.getLock("lock:" + key);
        try {
            if (lock.tryLock(10, 10, TimeUnit.SECONDS)) {
                try {
                    Object availableObj = redisTemplate.opsForValue().get(key);
                    Long available = availableObj != null ? Long.parseLong(availableObj.toString()) : 0L;

                    if (available > 0) {
                        redisTemplate.opsForValue().increment(key, -1); // 减少库存
                        redisTemplate.opsForSet().add(lockKey, outTradeNo); // 记录锁定
                        return true;
                    }
                    return false;
                } finally {
                    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
            return false;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    /**
     * 恢复库存（支付失败或超时，带分布式锁）
     */
    public void rollbackStock(Long physicalId, LocalDate date, String period, String outTradeNo) {
        String key = buildInventoryKey(physicalId, date, period);
        String lockKey = "inventory_lock:" + key;

        RLock lock = redissonClient.getLock("lock:" + key);
        try {
            if (lock.tryLock(10, 10, TimeUnit.SECONDS)) {
                try {
                    if (redisTemplate.opsForSet().isMember(lockKey, outTradeNo)) {
                        redisTemplate.opsForValue().increment(key, 1); // 恢复库存
                        redisTemplate.opsForSet().remove(lockKey, outTradeNo); // 移除锁定
                    }
                } finally {
                    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 支付成功时清理锁定记录
     */
    public void confirmStockDeduction(Long physicalId, LocalDate date, String period, String outTradeNo) {
        String lockKey = "inventory_lock:" + buildInventoryKey(physicalId, date, period);
        redisTemplate.opsForSet().remove(lockKey, outTradeNo);
    }

    /**
     * 单次初始化库存（运行时调用）
     */
    private void initializeInventoryIfAbsent(String key, Long physicalId) {
        if (!redisTemplate.hasKey(key)) {
            Physical physical = physicalMapper.selectWnjkPhysicalExaminationPackageById(physicalId);
            if (physical != null && physical.getIsAvailable() == 1) {
                Long total = physical.getTotal() != null ? physical.getTotal().longValue() : 0L;
                redisTemplate.opsForValue().set(key, total.toString());
                log.info("【单次初始化】初始化键: {}，库存: {}", key, total);
            } else {
                throw new IllegalStateException("无效的体检套餐ID: " + physicalId);
            }
        }
    }

    /**
     * 构建库存键
     */
    private String buildInventoryKey(Long physicalId, LocalDate date, String period) {
        return "inventory:" + physicalId + ":" + date + ":" + period;
    }
}